#pragma once
#include<iostream>
#include<string>
#include <sys/poll.h>
#include<sys/select.h>
#include<memory>
#include <unistd.h>
#include<poll.h>
#include "InetAddr.hpp"
#include "Log.hpp"

#include"Socket.hpp"

using namespace SocketModule;

const int gdefaultfd = -1;
#define MAX 4096

//仅关心读文件描述符集
class PollServer
{
public:
    PollServer(int port)
    :_port(port)
    ,_isrunning(false)
    ,_listen_socket(std::make_unique<TcpSocket>())
    {}

    //初始化服务器
    void Init()
    {
        //创建并完成listentcp套接字的整个过程
        _listen_socket->BuildTcpSocketMethod(_port);
        for(int i = 0;i < MAX;i++)
        {
            _fds[i].fd = gdefaultfd;
            _fds[i].events = _fds[i].revents = 0;
        }
        _fds[0].fd = _listen_socket->Fd();
        _fds[0].events |= POLLIN;
       
    }


    // 我们不能让accept来阻塞检测新连接到来，而应该让select来负责进行就绪事件的检测
    // 用户告诉内核，你要帮我关心&rfds，读事件啊！！

    //int select(int nfds, fd_set *readfds, fd_set *writefds,fd_set *exceptfds, struct timeval *timeout);
    void Select_dpa()
    {
        int timeout = -1;
        //等待
        int n = ::poll(_fds,MAX,timeout);
        //等待结果分类
        switch(n)
        {
            case 0:
                std::cout << "time out" << std::endl;
                break;
            case -1:
                perror("poll");
                break;
            default:
            // 有事件就绪了
            // rfds: 内核告诉用户，你关心的rfds中的fd，有哪些已经就绪了！！
                std::cout << "有事件就绪了..." << std::endl;
                Dispather();
                break;
        }
    }

    //服务器启动并进入循环
    void Startloop()
    {
        //创建读文件描述符集
        fd_set rfds;
        _isrunning = true;

        //进入循环
        while(_isrunning)
        {
            //利用Select进行等待，就绪后分派任务：
            //1.listen就绪获取链接
            //2.普通读取
            Select_dpa();
        }

        //循环结束，状态终止
        _isrunning = false;
    }


    //获取连接
    void Accepter()
    {
        InetAddr client;
        // listensockfd就绪了！获取新连接不就好了吗？
        int newfd = _listen_socket->Accepter(&client); // 会不会被阻塞呢？不会！poll已经告诉我，listensockfd已经就绪了！只执行"拷贝"
        if (newfd < 0)
            return;
        else
        {
            std::cout << "获得了一个新的链接: " << newfd << " client info: " << client.Addr() << std::endl;
            // recv()?? 读事件是否就绪，我们并不清楚！newfd也托管给select，让select帮我进行关心新的sockfd上面的读事件就绪
            // 怎么把新的newfd托管给POLL?让select帮我去关心newfd上面的读事件呢？把newfd，添加到_fds即可！
            int pos = -1;
            for(int i = 0;i < MAX;i++)
            {
                if(_fds[i].fd == gdefaultfd)//该位置没有被使用
                {
                    pos = i;
                    break;
                }
            }
            if(pos == -1)
            {
                LOG(LogLevel::ERROR) << "服务期满载...";
                close(newfd);
            }
            else
            {
                _fds[pos].fd= newfd;
                _fds[pos].events |= POLLIN;
            }
        }
    }

    void Recver(int who)
    {
        // 合法的，就绪的，普通的fd
        // 这里的recv，对不对啊！不完善！必须得有协议！
        char buffer[1024];
        ssize_t n = ::recv(_fds[who].fd,buffer,sizeof(buffer) - 1,0);
        if(n > 0)
        {
            buffer[n] = 0;
            std::cout << "client# :" << buffer << std::endl;
            //读完后回显给客户端
            std::string message("echo# :");
            message += buffer;
            ::send(_fds[who].fd,message.c_str(),message.size(),0);//返回不完善，完整的读写需要协议，这里存在bug
        }
        else if(n ==0)
        {
            LOG(LogLevel::DEBUG) << "客户端退出...,socketfd: " << _fds[who].fd;
            close(_fds[who].fd);
            _fds[who].fd = gdefaultfd;
            _fds[who].events = _fds[who].revents = 0;
        }
        else
        {
            LOG(LogLevel::DEBUG) << "客户端读取出错, sockfd: " << _fds[who].fd;
            close(_fds[who].fd);
            _fds[who].fd = gdefaultfd;
            _fds[who].events = _fds[who].revents = 0;
        }
    }
    //分配任务
    void Dispather()
    {
        //寻找合法fd
        for(int i = 0; i < MAX;i++)
        {
            if(_fds[i].fd == gdefaultfd)
                continue;
            //fd合法，判断：
            if(_fds[i].fd == _listen_socket->Fd())
            {
                //fd在读文件描述符集里
                if(_fds[i].revents & POLLIN)
                {
                    //litenfd获取链接
                    Accepter();
                }
            }
            else 
            {
                if(_fds[i].revents & POLLIN)
                {
                    //普通读取
                    Recver(i);//IO
                }
            }
        }
    }
    ~PollServer()
    {}
private:
    bool _isrunning;
    uint16_t _port;
    std::unique_ptr<Socket> _listen_socket;
    struct pollfd _fds[MAX];
   
};